<?php
/* 
 * openapi 传输加密类 客户端
 * @date 2015-12-08
 * @modifydate 2015-12-09
 * @author wzl
 * 使用方法：请求用send 函数, 接受用seceive 函数
 */
class OpenApiClient {
    /**定义新数组*/
    protected $values = array();
    protected static $appid = 'f6d5dc63e5d8'; //官方提供
    protected static $appkey = 'c2e935f15967c295c864e66a428e9bde';//官方提供
    /**
     * 初使化
     * @param string $key 【可选】设置密钥，为空则读取默认的
     */
    public function __construct() {
        date_default_timezone_set('PRC');//初使北京时间
    }
    public static function load() {
        return new self();
    }

    /**
     * 获取curl 请求过来的数据。
     * @return string 
     */
    public function getRequest(){
        return file_get_contents("php://input");
    }
    /**
     * 获取整个数组
     * @return array 返回数组
     */
    public function getValues(){
        return $this->values;
    }
    /**
     * 返回请求的data数据
     * @return mix 返回请求的数据
     */
    public function getData() {
        return $this->values['data'];
    }
    /**
     * 获取数组中的SIGN
     * @return string 返回大写的SIGN
     */
    public function getSign() {
        return $this->values['sign'];
    }
    /**
     * 设置需要传输的数据
     * @param array $data 数组，不限维度
     */
    public function setData($data) {
        $this->values['data'] = $data;
    }
    
    /**
     * 设置SIGN
     * @return string 返回大写的SIGN
     */
    public function setSign(){
        $sign = $this->makeSign();
        $this->values['sign'] = $sign;        
        return $sign;
    }
    /**
     * 将JSON转换成ARRAY
     * @param string $json 数据
     * @return array 数组
     */
    public function ToArray($json){
        $this->values = json_decode($json, true);
        return $this->values;
    }
    /**
     * 将ARRAY转换换JSON
     * @return string json数据
     */
    public function toJson() {
        ksort($this->values);
        return json_encode($this->values);
    }  
    /**
     * 生成SIGN
     * @return string 返回大写SIGN
     */
    public function makeSign() {    
        $param_arr = $this->toOneArray();
        ksort($param_arr);//词典排序
        $param_string = $this->toString($param_arr);//将数组转换成字符串
        $sign = md5($param_string . md5(self::$appkey));//加密形成SIGN
        return strtoupper($sign);//转换成大写
    }
    /**
     * 将数组拼接成字符串
     * @param array $params 数组数据
     * @return string
     */
    protected function toString(array $params) {
        if(is_array($params)) {
            $string = '';
            foreach ($params as $key => $value) {
                if($key != 'sign') {//排除sign，不参与加密 
                    $string = $key . '&' . $value;
                }
            }
            $new_string = trim($string, '&');
            return $new_string;
        }
        return '';
    }

    /**
     * 设置唯一变化值。以防攻击 
     */
    public function setOnly(){
        $this->setTime();
        $this->setNonce();
    }
    /**
     * 设置时间
     */
    protected function setTime(){
        $this->values['time'] = time();
    }
    /**
     * 设置随机码 
     */
    protected function setNonce(){
        $this->values['nonce'] = $this->getNonceStr();
    }
    /**
     * 设置返回值，
     * @param int $status 返回状态值 0为成功，其它值异常
     * @param mix $data 【可选】返回的数据
     * @return string 返回的json值 （已将array转换成json）
     */
    public function setReturn($status, $data = array()) {
        $return = array(
            'status'=> $status,
            'data'  => $data, 
        );
        return json_encode($return);
    }
    /**
     * 设置APPID和APPKEY
     * @param string $appid appid
     * @param string $appkey appkey
     */
    public function setAppIDAndAppKey($appid, $appkey) {
        self::$appid = $appid;
        self::$appkey = $appkey;
        $this->values['appid'] = $appid;        
    }
    /**
    * 
    * 产生随机字符串，不长于32位
    * @param int $length
    * @return 产生的随机字符串
    */
    protected static function getNonceStr($length = 32) 
    {
        $chars = "abcdefghijklmnopqrstuvwxyz0123456789";  
        $str ="";
        for ( $i = 0; $i < $length; $i++ )  {  
                $str .= substr($chars, mt_rand(0, strlen($chars)-1), 1);  
        } 
        return $str;
    }
    /**
     * 将多维数组转换成一维数组
     */
    protected function toOneArray() {
        $param_arr = array();//新定义一个数组
        foreach ($this->values as $key => $val) {
            if(is_array($val)) {//数组的则进行序列化
                $param_arr[$key] = serialize($val);
            } else {//非数组的不序列化
                $param_arr[$key] = $val;
            }
        }
        return $param_arr;
    }
    /**
     * curl 传输数据
     * @param string $url 有效url
     * @param string $json json数据
     * @return string 结果
     */
    public function postJsonCurl($url, $json) {
        $ch = curl_init();
        curl_setopt($ch, CURLOPT_URL, $url);
        curl_setopt($ch, CURLOPT_POST, 1);
        curl_setopt($ch, CURLOPT_POSTFIELDS, $json);
        curl_setopt($ch, CURLOPT_TIMEOUT_MS, 5000);
        curl_setopt($ch,  CURLOPT_RETURNTRANSFER, true);
        $result = curl_exec($ch);
        curl_close ($ch);
        return $result;
    }
    /**
     * 向远程请求的综合方法
     * @param string $url 请求的URL 可以再拼接act=xxx
     * @param mix $data 需要传输的数据。
     * @return array 已解压的数据
     */
    public function send($url, $data){
        $openapi = new self();//先实例apibase
        $openapi->setAppIDAndAppKey(self::$appid, self::$appkey);//设置appid,appkey
        $openapi->setOnly();//设置唯一变化值，以防攻击
        $openapi->setData($data);//设置需要传输的数据，可任意数据
        $openapi->setSign();//生成签名SIGN，必须在setData后执行
        mylog($openapi->getValues(), 'send_data');
        $json = $openapi->toJson();//生成json
        $json_result = $openapi->postJsonCurl($url, $json);//curl传输
        return json_decode($json_result, true);//获取结果
    }
}